#include "config.h"

#include <sys/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define DBG_SUBSYS S_LIBYLIB

#include "sysutil.h"
#include "sysy_lib.h"
#include "mem_pool.h"
#include "hash_table.h"
#include "configure.h"
#include "schedule.h"
#include "dbg.h"
#include "core.h"

typedef struct {
        uint64_t malloc_bytes;
        uint64_t malloc_count;
        uint64_t free_count;
} ymalloc_entry_t;

typedef enum {
        YMALLOC_DEF = 0,
        YMALLOC_ROW2,
        YMALLOC_MAX
} ymalloc_type_t;

typedef struct {
        ymalloc_entry_t entries[YMALLOC_MAX];
} ymalloc_stat_t;

ymalloc_stat_t ymalloc_stat;

void ymalloc_print() {
        for (int i=0; i < YMALLOC_MAX; i++) {
                DWARN("%d malloc %ju %ju free %ju diff %ju\n",
                      i,
                      ymalloc_stat.entries[i].malloc_bytes / 1024 / 1024,
                      ymalloc_stat.entries[i].malloc_count,
                      ymalloc_stat.entries[i].free_count,
                      ymalloc_stat.entries[i].malloc_count - ymalloc_stat.entries[i].free_count);
        }
}

int ymalloc(void **_ptr, size_t size)
{
        int ret, i;
        void *ptr;

        YASSERT(size != 0);

        if (size == 0) {
                *_ptr = NULL;
                return 0;
        }

        if (size > 4096)
                DBUG("big mem %u\n", (int)size);

        for (i = 0; i < 3; i++) {
                ptr = calloc(1, size);
                if (ptr != NULL)
                        goto out;
        }

        ret = ENOMEM;

        GOTO(err_ret, ret);
out:
        *_ptr = ptr;
       /* if (core_self()) {
                DWARN("calloc'ed mem %p, size %lu\n", ptr, (unsigned long)size);
               // _backtrace("ymalloc");
        } */
#if ENABLE_YMALLOC_STAT
        ymalloc_stat.entries[0].malloc_bytes += size;
        ymalloc_stat.entries[0].malloc_count += 1;
#endif

        return 0;
err_ret:
        return ret;
}

int yrealloc(void **_ptr, size_t size, size_t newsize)
{
        int ret, i;
        void *ptr;

        if (*_ptr == NULL && size == 0) /*malloc*/ {
                ret = ymalloc(&ptr, newsize);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                _memset(ptr, 0x0, newsize);

                *_ptr = ptr;
                return 0;
        }

        if (newsize == 0)
                return yfree(_ptr);

        if (newsize < size) {
                ptr = *_ptr;
                _memset(ptr + newsize, 0x0, size - newsize);
        }

        ret = ENOMEM;
        for (i = 0; i < 3; i++) {
                ptr = realloc(*_ptr, newsize);
                if (ptr != NULL)
                        goto out;
        }

        GOTO(err_ret, ret);
out:
        if (newsize > size)
                _memset(ptr + size, 0x0, newsize - size);

        *_ptr = ptr;

        return 0;
err_ret:
        return ret;
}

inline int yfree(void **ptr)
{
        /*if (core_self()) {
                DWARN("free'ed mem %p, size %lu\n", *ptr, sizeof(*ptr));
                _backtrace("yfree");
        } */
        if (*ptr != NULL) {
                free(*ptr);
        } else {
                YASSERT(0);
        }

        *ptr = NULL;

#if ENABLE_YMALLOC_STAT
        ymalloc_stat.entries[0].free_count += 1;
#endif

        return 0;
}

#if 1
int ymalloc1(void **_ptr, size_t size)
{
        int ret, i;
        void *ptr;

        YASSERT(size != 0);

        if (size == 0) {
                *_ptr = NULL;
                return 0;
        }

        if (size > 4096)
                DBUG("big mem %u\n", (int)size);

        for (i = 0; i < 3; i++) {
                ptr = calloc(1, size);
                if (ptr != NULL)
                        goto out;
        }

        ret = ENOMEM;

        GOTO(err_ret, ret);
out:
        *_ptr = ptr;

        return 0;
err_ret:
        return ret;
}

int yfree1(void **ptr)
{

        //DBUG("free'ed mem %p, size %u\n", ptr, sizeof(*ptr));
        if (*ptr != NULL) {
                free(*ptr);
        } else  {
                 YASSERT(0);
        }

        *ptr = NULL;

        return 0;
}

#endif
